﻿using Furion.CMS.Application.Dto;
using Furion.CMS.Core;
using Furion.CMS.Core.EventSource;
using Furion.CMS.Domain;
using Furion.CMS.EntityFramework.Core;
using Furion.DatabaseAccessor;
using Furion.DataEncryption.Extensions;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Furion.CMS.Application
{
    /// <summary>
    /// 用户应用服务
    /// </summary>
    [DisplayName("UserManager")]
    public class UserAppService : CurdAuditAppService<UserInfo, int,
            UserCreateDto,
            UserModifyDto,
            UserDto,
            UserDetailDto,
            UserFilter,
            UserSort>, IScoped
    {
        private readonly IRepository<RoleInfo> _roleRepository;
        private readonly IRepository<UserPassport> _passportRepository;
        private readonly IRepository<PermissionInfo> _permissionRepository;
        private readonly IRepository<OrganizationInfo> _orgRepository;

        public UserAppService(IRepository<UserInfo> userRepository,
            IRepository<RoleInfo> roleRepository,
            IRepository<UserPassport> passportRepository,
            IRepository<PermissionInfo> permissionRepository,
            IRepository<OrganizationInfo> orgRepository)
            : base(userRepository)
        {
            _roleRepository = roleRepository;
            _passportRepository = passportRepository;
            _permissionRepository = permissionRepository;
            _orgRepository = orgRepository;
        }

        protected override IQueryable<UserInfo> FilterBeforeQueryHandle(IQueryable<UserInfo> entities)
        {
            return entities.Include(i => i.Organizations);
        }

        /// <summary>
        /// 验证指定的字段内容是否存在
        /// </summary>
        /// <param name="fieldName"></param>
        /// <param name="fieldValue"></param>
        /// <param name="ignoreId"></param>
        /// <returns></returns>
        [HttpGet]
        [QueryParameters]
        public async Task CheckIsRedundantAsync(string fieldName, string fieldValue, int? ignoreId = null)
        {
            var personQuery = _appRepository.AsQueryable(false).Where(ignoreId.HasValue, t => t.Id != ignoreId);
            var passportQuery = _passportRepository.AsQueryable(false).Where(ignoreId.HasValue, t => t.UserId != ignoreId);

            switch (fieldName)
            {
                case nameof(UserInfo.NickName):
                    if (await personQuery.AnyAsync(t => t.NickName.Equals(fieldValue)))
                    {
                        throw Oops.Oh("当前昵称已存在，请重新输入");
                    }
                    break;
                case nameof(UserInfo.Email):
                    if (await personQuery.AnyAsync(t => t.Email.Equals(fieldValue)))
                    {
                        throw Oops.Oh("当前电子邮件已存在，请重新输入");
                    }
                    break;
                case nameof(UserInfo.PhoneNumber):
                    if (await personQuery.AnyAsync(t => t.PhoneNumber.Equals(fieldValue)))
                    {
                        throw Oops.Oh("当前手机号已存在，请重新输入");
                    }
                    break;
                case nameof(UserPassport.Account):
                    if (await passportQuery.AnyAsync(t => t.Account.Equals(fieldValue)))
                    {
                        throw Oops.Oh("当前账号已存在，请重新输入");
                    }
                    break;
            }
        }

        /// <summary>
        /// 获取全部人员
        /// </summary>
        /// <returns></returns>
        public async Task<List<UserDto>> GetAllAsync()
        {
            return await _appRepository.AsQueryable(false).ProjectToType<UserDto>().ToListAsync();
        }

        protected override IQueryable<UserInfo> DetailFilterEndQueryHandle(IQueryable<UserInfo> entities)
        {
            // 加载通行证,角色,组织机构信息
            entities = entities
                .Include(i => i.Passports)
                .Include(i => i.Roles)
                .Include(i => i.Organizations)
                .ThenInclude(i => i.Roles)
                .AsSplitQuery();

            return entities;
        }

        //protected override UserDetailDto AfterGetDetail(UserInfo entity)
        //{
        //    var dto = base.AfterGetDetail(entity);

        //    dto.Roles = entity.Roles.UnionBy(entity.Organizations.SelectMany(s => s.Roles), u => u.Id).Select(s => s.Id).ToList();

        //    return dto;
        //}

        /// <summary>
        /// 添加用户
        /// </summary>
        /// <param name="dto"></param>
        [UnitOfWork]
        [SecurityDefine]
        public override void Create([FromBody] UserCreateDto dto)
        {
            var entity = dto.Adapt<UserInfo>();

            // 处理账号信息
            entity.Passports.Add(new UserPassport
            {
                Account = dto.Account,
                Password = dto.Password,
                Type = PassportType.Normal,
                Enable = true,
                RegionRestrict = PassportRegionRestrict.Admin
            });
            // 先提交数据库数据
            _appRepository.InsertNow(entity);

            // 处理关联角色
            var roleList = _roleRepository.Where(t => dto.Roles.Contains(t.Id), false).ToList();
            entity.Roles = roleList;
            // 处理关联机构
            var orgList = _orgRepository.Where(t => dto.Orgs.Contains(t.Id), false).ToList();
            entity.Organizations = orgList;
        }

        /// <summary>
        /// 更新用户
        /// </summary>
        /// <param name="id"></param>
        /// <param name="dto"></param>
        [UnitOfWork]
        [SecurityDefine]
        public override void Update([ApiSeat(ApiSeats.ActionStart)] int id, [FromBody] UserModifyDto dto)
        {
            // 获取历史人员信息
            var history = _appRepository.AsQueryable(false)
                .Where(t => t.Id.Equals(id))
                .FirstOrDefault();
            if (history == null) throw Oops.Oh($"未能找到ID为 {id} 的人员数据");

            var entity = dto.Adapt<UserInfo>();

            // 更新基础信息
            var update = base.BeforeUpdate(dto, history, entity, "HasSeed");
            var updateEntry = _appRepository.Update(update);
            // 加载关联查询用于更新
            updateEntry.Collection(c => c.Roles).Load();
            updateEntry.Collection(c => c.Organizations).Load();
            updateEntry.Collection(c => c.Passports).Load();

            // 处理角色
            var updateRoles = _roleRepository.Where(t => dto.Roles.Contains(t.Id), false).ToList();
            update.Roles.ToCollectionChange(updateRoles, f => f.Id);
            // 处理机构
            var updateOrgs = _orgRepository.Where(t => dto.Orgs.Contains(t.Id), false).ToList();
            update.Organizations.ToCollectionChange(updateOrgs, f => f.Id);
            // 处理通行证
            foreach (var passport in update.Passports)
            {
                var changePassport = dto.Passports.FirstOrDefault(f => f.Id == passport.Id);
                if (changePassport != null && passport.Enable != changePassport.Enable)
                {// 通行证存在且启用状态不一致才更新
                    passport.Enable = changePassport.Enable;
                    passport.UpdatedTime = DateTimeOffset.UtcNow;
                }

                // 若主体被禁用了则所有通行证均更新为禁用状态
                if (!update.Enable)
                {
                    passport.Enable = false;
                    passport.UpdatedTime = DateTimeOffset.UtcNow;
                }
            }
        }

        /// <summary>
        /// 删除一个或多个用户
        /// </summary>
        /// <param name="ids"></param>
        [UnitOfWork]
        [SecurityDefine]
        public override void Delete([FromBody] IList<int> ids)
        {
            var deleteEntities = _appRepository.AsQueryable(false).Where(t => ids.Contains(t.Id))
                .Include(i => i.Passports)
                .ToList();

            // 获取当前删除人员中不能删除的人列表
            var canNotDeleteList = deleteEntities.Where(t => t.HasSeed).Select(s => s.NickName);

            if (canNotDeleteList.Any())
            {
                throw Oops.Oh($"{string.Join(",", canNotDeleteList)} 为内置人员,无法删除");
            }

            _appRepository.Delete(deleteEntities);
        }

        /// <summary>
        /// 获取通行证信息
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [SecurityDefine]
        public List<UserPassportDto> GetPassports([ApiSeat(ApiSeats.ActionStart)] int id)
        {
            var entity = _appRepository.Where(t => t.Id == id)
                   .Include(i => i.Passports)
                   .FirstOrDefault();

            if (entity == null)
                throw Oops.Oh($"未能找到ID为 {id} 的数据");

            return entity.Passports.Adapt<List<UserPassportDto>>();
        }

        /// <summary>
        /// 更新通行证状态
        /// </summary>
        /// <param name="id"></param>
        /// <param name="changeDto"></param>
        [HttpPatch]
        [UnitOfWork]
        [SecurityDefine]
        public void UpdatePassportState([ApiSeat(ApiSeats.ActionStart)] int id, [FromBody] UserPassportStateChangeDto changeDto)
        {
            var entity = _appRepository.Where(t => t.Id == id)
                   .Include(i => i.Passports)
                   .FirstOrDefault();

            if (entity == null)
                throw Oops.Oh($"未能找到ID为 {id} 的数据");

            var passport = entity.Passports.FirstOrDefault(t => t.Id == changeDto.Id);

            if (passport == null)
                throw Oops.Oh("未能找指定的通行证信息");

            passport.Enable = changeDto.Enable;
            passport.UpdatedTime = DateTimeOffset.UtcNow;
        }

        /// <summary>
        /// 修改密码
        /// </summary>
        /// <param name="id"></param>
        /// <param name="newPassword"></param>
        /// <returns></returns>
        [HttpPatch]
        [UnitOfWork]
        [SecurityDefine]
        public void UpdataPassword([ApiSeat(ApiSeats.ActionStart)] int id, string newPassword)
        {
            var entity = _appRepository.Where(t => t.Id == id)
                .Include(i => i.Passports)
                .FirstOrDefault();

            if (entity == null)
                throw Oops.Oh($"未能找到ID为 {id} 的数据");

            // 获取普通账号通行证信息
            var normalPassport = entity.Passports.FirstOrDefault(t => t.Type == PassportType.Normal);

            if (normalPassport == null)
                throw Oops.Oh($"{entity.NickName}的账号信息不存在");

            // 更改密码与更新时间
            normalPassport.Password = newPassword;
            normalPassport.UpdatedTime = DateTimeOffset.UtcNow;
        }

        /// <summary>
        /// 获取权限
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [SecurityDefine]
        public async Task<List<string>> GetPermissions([ApiSeat(ApiSeats.ActionStart)] int id)
        {
            var entity = await _appRepository.AsQueryable(false)
                   .Where(t => t.Id == id)
                   /* 获取角色授予权限 */
                   .Include(i => i.Roles)
                      .ThenInclude(i => i.Permissions)
                   /* 获取机构授予权限 */
                   .Include(i => i.Organizations)
                      .ThenInclude(i => i.Roles)
                         .ThenInclude(i => i.Permissions)
                   /* 获取独立授予权限 */
                   .Include(i => i.Permissions)
                   .AsSplitQuery()
                   .FirstOrDefaultAsync();

            if (entity == null) throw Oops.Oh($"未能找到ID为 {id} 的数据");

            var rolePermissions = entity.Roles.SelectMany(s => s.Permissions);
            var orgPermissions = entity.Organizations.SelectMany(s => s.Roles.SelectMany(m => m.Permissions));
            var singlePermissions = entity.Permissions;

            // 合并为结果
            var result = rolePermissions
                .UnionBy(orgPermissions, t => t.Code)
                .UnionBy(singlePermissions, t => t.Code)
                .Select(s => s.Code)
                .ToList();

            return result;
        }

        /// <summary>
        /// 更新权限
        /// </summary>
        /// <param name="id"></param>
        /// <param name="permissionCodes"></param>
        [UnitOfWork]
        [SecurityDefine]
        public void UpdatePermissions([FromServices] IEventPublisher eventPublisher, [ApiSeat(ApiSeats.ActionStart)] int id, [FromBody] List<string> permissionCodes)
        {
            var entity = _appRepository.Where(t => t.Id == id).Include(i => i.Permissions).FirstOrDefault();

            if (entity == null) throw Oops.Oh($"未能找到ID为 {id} 的数据");

            var permissionList = _permissionRepository
                .Where(t => permissionCodes.Contains(t.Code))
                .Include(i => i.Parent)
                .ToList();

            // 去掉继承的权限项
            entity.Permissions.ToCollectionChange(permissionList, f => f.Code, out List<PermissionInfo> insertItems, out List<PermissionInfo> removeItems);

            // 通知前端更换token
            eventPublisher?.PublishDelayAsync(new OnTriggerOuterEvent(OuterEventCategory.UserRefresh, id.ToString()), 1000);
        }
    }
}
